/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */

package com.liycloud.common.security.component;

import cn.hutool.core.util.StrUtil;
import com.liycloud.common.core.constant.SecurityConstants;
import com.liycloud.common.security.annotation.Inner;
import com.liycloud.common.security.util.PigxSecurityMessageSourceUtil;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.access.AccessDeniedException;

import javax.servlet.http.HttpServletRequest;

/**
 *
 *  参考:   https://pig4cloud.com/doc/pigx/pigx-inner
 *
 * @author lengleng
 * @date 2018/11/26
 *
 * <p>
 * 服务间接口不鉴权处理逻辑:  使用的是 Spring-AOP 环绕通知
 *
 * 那上面讲到的，如果我们不希望这个url可以直接被外网调用，仅能在Feign服务中调用，改如何统一处理呢？
 *
 * 我们使用一个Spring-AOP，在对所有Inner注解的方法做一个环绕增强的切点，进行统一的处理。
 *
 * 在上面我们提到的Inner的value参数，当该参数为true时，我们对方法的入参进行判断，仅当符合我们定制的入参规则时
 *
 * （Pigx这里是用的@RequestHeader(SecurityConstants.FROM) 与SecurityConstants.FROM_IN做比较）, 我们对它进行放行，不符合时，抛出异常；
 *
 *  当value为false时，咱不做任何处理，此时Inner仅起到了一个 ignore-url 的作用, 相当于完全对内对外暴露的url
 *
 *  要是 外部接口访问也模拟加上 在请求头Header加上参数from=Y 怎么办 ?
 *
 *  这就是网关 gateWay 全局过滤清洗Filter过滤器:  PigxRequestGlobalFilter 的作用,清洗请求头中 from 参数
 *
 *  那么即使外部访问在请求头Header加上参数from=Y , 也会被清理掉
 *
 *
 */
@Slf4j
@Aspect
@AllArgsConstructor
public class PigxSecurityInnerAspect {

	private final HttpServletRequest request;

	@SneakyThrows
	@Around("@annotation(inner)")
	public Object around(ProceedingJoinPoint point, Inner inner) {

		// 从请求头Header 中获取参数:  from=Y     内部: String FROM_IN = "Y";
		String header = request.getHeader(SecurityConstants.FROM);


		// 标注的值 和 请求头是否一致,   当不是 FROM_IN 时 访问被拒绝 抛出异常
		if ( inner.value()  && !StrUtil.equals(SecurityConstants.FROM_IN, header)) {

			log.warn("访问接口 {} , 没有权限", inner.value() );

			throw new AccessDeniedException(
					PigxSecurityMessageSourceUtil.getAccessor().getMessage("AbstractAccessDecisionManager.accessDenied", new Object[] { inner.value() }, "access denied")
			);
		}
		return point.proceed();
	}

}
